﻿//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals.World;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;


namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    internal class ShadoPanMonastery : Dungeon
    {
        #region Overrides of Dungeon

        private readonly WoWPoint _azureSerpentFollowerLoc = new WoWPoint(3736.51, 2670.499, 768.0417);
        private readonly WoWPoint _azureSerpentTankLoc = new WoWPoint(3727.666, 2688.185, 768.0416);

        public override uint DungeonId
        {
            get { return 466; }
        }


        public override WoWPoint Entrance
        {
            get { return new WoWPoint(3643.886, 2544.806, 769.9496); }
        }

        public override bool IsComplete
        {
            get
            {
                var lordOfTheShadoPan = Me.QuestLog.GetQuestById(30757);
                var thePathToRespectLiesInViolence = Me.QuestLog.GetQuestById(31342);
                return BossManager.CurrentBoss == null && (lordOfTheShadoPan == null || !lordOfTheShadoPan.IsCompleted) &&
                       (thePathToRespectLiesInViolence == null || thePathToRespectLiesInViolence.IsCompleted);
            }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret.ToUnit();
                    if (unit != null)
                    {
                        if (unit.Entry == GuCloudstrikeId && unit.HasAura("Charging Soul"))
                            return true;
                        if (unit.Entry == ShadoPanNoviceId && unit.HasAura("Parry Stance") && unit.IsFacing(Me))
                            return true;
                        if (unit.Entry == MasterSnowdriftId && !Me.IsHealer())
                        {
                            if (unit.CastingSpellId == ParryStanceId || unit.HasAura(ParryStanceId))
                                return true;
                            if ((unit.HasAura(PursuitId) && unit.CurrentTargetGuid == Me.Guid || unit.CastingSpellId == TornadoKickId) && Me.IsMelee())
                                return true;
                        }
                        if (unit.Entry == LesserVolatileEnergyId &&
                            Me.GroupInfo.RaidMembers.Where(r => r.IsRange() && r.IsDps()).OrderByDescending(r => r.HealthMax).Select(r => r.Guid).FirstOrDefault() !=
                            Me.Guid)
                            return true;
                        // if (unit.HasAura("Apparitions") && Me.IsDps())
                        //     return true;

                        if (unit.Entry == AzureSerpentId && unit.HasAura("Lightning Shielded") && !Me.IsHealer())
                            return true;

                        if (unit.Entry == GrippingHatred && StyxWoW.Me.IsTank())
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == MasterSnowCopyId)
                        outgoingunits.Add(unit);
                    // kill any hatefull essences that are withing range of a slain shado Pan defender which is protecting the hostile units I'm in combat with atm
                    if (unit.Entry == HatefulEssenceId)
                    {
                        var immuneUnit = Targeting.Instance.TargetList.FirstOrDefault(t => t.HasAura("Apparitions"));
                        if (immuneUnit != null)
                        {
                            var defender = ObjectManager.GetObjectByGuid<WoWUnit>(immuneUnit.Auras["Apparitions"].CreatorGuid);
                            if (defender != null && defender.Location.Distance(unit.Location) <= 15)
                                outgoingunits.Add(unit);
                        }
                    }

                    if (unit.Entry == GrippingHatred && Me.IsDps() &&
                        (Me.IsRange() || Me.IsMelee() && ScriptHelpers.GetGroupMembersByRole(ScriptHelpers.GroupRoleFlags.Ranged).Count(p => p.IsAlive) < 3))
                        outgoingunits.Add(unit);

                    if (unit.Entry == LesserVolatileEnergyId &&
                        Me.GroupInfo.RaidMembers.Where(r => r.IsRange() && r.IsDps()).OrderByDescending(r => r.HealthMax).Select(r => r.Guid).FirstOrDefault() == Me.Guid)
                    {
                        outgoingunits.Add(unit);
                    }
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (StyxWoW.Me.IsDps())
                    {
                        if (unit.Entry == ShadoPanWardenId && unit.CastingSpellId == FocusEnergy)
                            priority.Score += 500;

                        if (unit.Entry == GrippingHatred)
                            priority.Score += 50000;
                    }

                    if (unit.Entry == HatefulEssenceId && Me.IsDps())
                        priority.Score += 50000;

                    if (unit.Entry == LesserVolatileEnergyId &&
                        Me.GroupInfo.RaidMembers.Where(r => r.IsRange() && r.IsDps()).OrderByDescending(r => r.HealthMax).Select(r => r.Guid).FirstOrDefault() == Me.Guid)
                    {
                        priority.Score += 50000;
                    }
                }
            }
        }

        public override MoveResult MoveTo(WoWPoint location)
        {
            if (!Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry == AzureSerpentId && location == Targeting.Instance.FirstUnit.Location)
            //&& Me.Location.DistanceSqr(_azureSerpentMeleeLoc) > 4 * 4)
            {
                if (Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentTankLoc) > 4 * 4)
                    return Navigator.MoveTo(_azureSerpentTankLoc);
                if (!Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentFollowerLoc) > 4 * 4)
                    return Navigator.MoveTo(_azureSerpentFollowerLoc);
                return MoveResult.Moved;
            }

            return base.MoveTo(location);
        }

        #endregion

        private const uint ShadoPanWardenId = 59751;
        private const uint GuCloudstrikeId = 56747;
        private const uint AzureSerpentId = 56754;
        private const uint FlyingSnowId = 56473;
        private const uint BallOfFireId = 59225;
        private const uint ShadoPanNoviceId = 56395;
        private const uint FireFlowerId = 56646;
        private const uint MasterSnowDriftsBallOfFireId = 56640;
        private const uint MasterSnowCopyId = 56713;
        private const int ParryStanceId = 106454;
        private const int FistsOfFuryId = 106853;
        private const int PursuitId = 106880;
        private const int TornadoKickId = 106434;
        private const int FocusEnergy = 115009;
        private const uint MasterSnowdriftId = 56541;
        private const int SmokeBladesId = 106826;
        private const uint ShaOfViolence = 56719;
        private const uint LesserVolatileEnergyId = 66652;
        private const uint HatefulEssenceId = 58812;
        private const uint GrippingHatred = 59804;
        private const uint ResidualHatredId = 58803;
        private const uint SlainShadoPanDefender = 58794;

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(0, "Root")]
        public Composite RootEncounter()
        {
            return
                new PrioritySelector(
                    ScriptHelpers.CreateRunAwayFromBad(ctx => !StyxWoW.Me.IsTank(), 7, u => u.Entry == FlyingSnowId && ((WoWUnit)u).HasAura("Whirling Steel")),
                    ScriptHelpers.CreateRunAwayFromBad(ctx => true, 10, BallOfFireId),
                    ScriptHelpers.CreateRunAwayFromBad(ctx => true, 5, FireFlowerId),
                // from shado-pan novices
                    ScriptHelpers.CreateDispellParty("Black Cleave", ScriptHelpers.PartyDispellType.Magic),
                // deal with Slain Shado-Pan Defender.
                    new Decorator(
                        ctx => Me.Combat && Me.IsDps(),
                        new PrioritySelector(
                            ctx =>
                            {
                                var immuneUnit = Targeting.Instance.TargetList.FirstOrDefault(t => t.HasAura("Apparitions"));
                                return immuneUnit != null ? ObjectManager.GetObjectByGuid<WoWUnit>(immuneUnit.Auras["Apparitions"].CreatorGuid) : null;
                            },
                            new Decorator<WoWUnit>(
                                defender =>
                                Me.PartyMembers.All(p => p.ChannelObject != defender) &&
                                !ScriptHelpers.GetUnfriendlyNpsAtLocation(() => defender.Location, 12, u => u.Entry == HatefulEssenceId).Any(),
                                new PrioritySelector(
                                    new Decorator<WoWUnit>(
                                        defender => defender.DistanceSqr > 4 * 4, new Helpers.Action<WoWUnit>(defender => Navigator.MoveTo(defender.Location))),
                                    new Decorator<WoWUnit>(
                                        defender => defender.DistanceSqr <= 4 * 4 && !Me.IsChanneling, new Helpers.Action<WoWUnit>(defender => defender.Interact())))))));
        }

        [EncounterHandler(62236, "Ban Bearheart", Mode = CallBehaviorMode.Proximity, BossRange = 20)]
        public Composite BanBearheartEncounter()
        {
            return new PrioritySelector(new Decorator<WoWUnit>(unit => unit.QuestGiverStatus == QuestGiverStatus.Available, ScriptHelpers.CreatePickupQuest(62236)));
        }

        [EncounterHandler(56747, "Gu Cloudstrike")]
        public Composite GuCloudstrikeEncounter()
        {
            WoWUnit boss = null;
            const uint staticFieldStalkerId = 56803;
            const int invokeLightningSpellId = 106984;
            const int staticFieldSpellId = 106923;

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                // run from static field
                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 10, staticFieldStalkerId),
                ScriptHelpers.CreateRunAwayFromLocation(
                    ctx => true, 10, m => ((WoWMissile)m).ImpactPosition, () => WoWMissile.InFlightMissiles.Where(m => m.SpellId == staticFieldSpellId)),
                // run from party member that is target with Invoke Lightning
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => true, 5, u => boss != null && boss.IsValid && boss.CastingSpellId == invokeLightningSpellId && u.Guid == boss.CurrentTargetGuid && u.Guid != Me.Guid),
                // stack up for aoe heals and to deal with the debuff in phase 2.
                new Decorator(
                    ctx => boss.HasAura("Charging Soul"),
                    new PrioritySelector(
                        new Decorator(ctx => Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentTankLoc) > 10 * 10, new Action(ctx => Navigator.MoveTo(_azureSerpentTankLoc))),
                        new Decorator(
                            ctx => !Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentFollowerLoc) > 10 * 10, new Action(ctx => Navigator.MoveTo(_azureSerpentFollowerLoc))))));
        }

        [EncounterHandler(56541, "Master Snowdrift", Mode = CallBehaviorMode.Proximity)]
        public Composite MasterSnowdriftEncounter()
        {
            var roomCenter = new WoWPoint(3713.436, 3091.417, 817.3193);

            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(
                    ctx => boss.IsFriendly && ScriptHelpers.IsBossAlive("Master Snowdrift"),
                    new Action(
                        ctx =>
                        {
                            BossManager.BossEncounters.FirstOrDefault(b => b.Entry == MasterSnowdriftId).MarkAsDead();
                            if (ScriptHelpers.IsBossAlive("Gu Cloudstrike"))
                                BossManager.BossEncounters.FirstOrDefault(b => b.Entry == GuCloudstrikeId).MarkAsDead();
                        })),
                // pickup quest.
                new Decorator<WoWUnit>(unit => unit.QuestGiverStatus == QuestGiverStatus.Available, ScriptHelpers.CreatePickupQuest(56541)),
                new Decorator<WoWUnit>(
                    unit => unit.Combat,
                    new PrioritySelector(
                // move into room as soon as encounter starts.
                        new Decorator(ctx => Me.Location.DistanceSqr(roomCenter) > 30 * 30, new Action(ctx => Navigator.MoveTo(roomCenter))),
                        ScriptHelpers.CreateRunAwayFromBad(
                            ctx => true,
                            4,
                            u => u.Entry == MasterSnowDriftsBallOfFireId,
                            u =>
                            {
                                var start = u.Location;
                                return Me.Location.GetNearestPointOnSegment(start, start.RayCast(WoWMathHelper.NormalizeRadian(u.Rotation), 20));
                            }),
                     ScriptHelpers.CreateRunAwayFromBad(
                            ctx => true, 6, u => u.Entry == MasterSnowdriftId && u.ToUnit().CastingSpellId == FistsOfFuryId, u => u.Location.RayCast(u.Rotation, 3)),

                        ScriptHelpers.CreateRunAwayFromBad(
                            ctx => true, () => roomCenter, 17, 10, u => u.Entry == MasterSnowdriftId && ((WoWUnit)u).CastingSpellId == TornadoKickId),
                        ScriptHelpers.CreateRunAwayFromBad(
                            ctx => true,
                            () => roomCenter,
                            17,
                            15,
                            u => u.Entry == MasterSnowdriftId && ((WoWUnit)u).HasAura(PursuitId) && ((WoWUnit)u).CurrentTargetGuid == Me.Guid),
                        new Decorator(
                            ctx => (boss.CastingSpellId == ParryStanceId || boss.HasAura(ParryStanceId)) && !Me.IsHealer() && Me.CurrentTargetGuid > 0,
                            new Action(ctx => Me.ClearTarget())),
                // don't go anywhere if target list is empty.
                        new Decorator(ctx => Me.IsTank() && Targeting.Instance.IsEmpty(), new ActionAlwaysSucceed()))));
        }

        [ObjectHandler(214518, "Snowdrift's Possessions")]
        [ObjectHandler(214519, "Snowdrift's Possessions")]
        public Composite SnowdriftsPossessionsHandler()
        {
            WoWGameObject chest = null;
            return new PrioritySelector(ctx => chest = ctx as WoWGameObject, new Decorator(ctx => chest.CanLoot, ScriptHelpers.CreateInteractWithObject(() => chest)));
        }


        [EncounterHandler(56764, "Consuming Sha")]
        public Composite ConsumingShaEncounter()
        {
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                ScriptHelpers.CreateDispellParty("Consumption", ScriptHelpers.PartyDispellType.Magic),
                ScriptHelpers.CreateDispellEnemy("Consuming Bite", ScriptHelpers.EnemyDispellType.Enrage, () => unit));
        }

        [EncounterHandler(56763, "Regenerating Sha")]
        public Composite RegeneratingShaEncounter()
        {
            const int RegenerateSpellId = 106920;

            WoWUnit unit = null;
            return new PrioritySelector(ctx => unit = ctx as WoWUnit, ScriptHelpers.CreateInterruptCast(() => unit, RegenerateSpellId));
        }

        [EncounterHandler(56765, "Destroying Sha")]
        public Composite DestroyingShaEncounter()
        {
            WoWUnit unit = null;
            const int shadowsOfDestruction = 106942;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                ScriptHelpers.CreateInterruptCast(() => unit, shadowsOfDestruction),
                ScriptHelpers.GetBehindUnit(
                    ctx => unit.CastingSpellId == shadowsOfDestruction && !Me.IsTank() && unit.IsSafelyFacing(Me, 65) && unit.DistanceSqr <= 15 * 15, () => unit));
        }

        [EncounterHandler(56719, "Sha of Violence")]
        public Composite ShaofViolenceEncounter()
        {
            var roomCenter = new WoWPoint(3996.92, 2905.266, 770.3101);
            const int shaSpikeId = 106877;
            var shaSpikeTimer = new WaitTimer(TimeSpan.FromSeconds(5));
            var shaSpikeLoc = WoWPoint.Zero;

            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => Me.IsRange(), () => roomCenter, 25, 12, u => u.Entry == ShaOfViolence && ((WoWUnit)u).CastingSpellId == SmokeBladesId),
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => boss != null && boss.IsValid && boss.CastingSpellId == shaSpikeId,
                    5,
                    u => boss != null && boss.IsValid && boss.CurrentTargetGuid == u.Guid,
                    t =>
                    {
                        if (shaSpikeTimer.IsFinished)
                        {
                            shaSpikeLoc = t.Location;
                            shaSpikeTimer.Reset();
                        }
                        return shaSpikeLoc;
                    }),
                ScriptHelpers.CreateDispellEnemy("Enrage", ScriptHelpers.EnemyDispellType.Enrage, () => boss),
                ScriptHelpers.CreateDispellParty("Disorienting Smash", ScriptHelpers.PartyDispellType.Magic));
        }

        [EncounterHandler(58810, "Fragment of Hatred")]
        public Composite FragmentOfHatredEncounter()
        {
            WoWUnit unit = null;
            const int volleyOfHatred = 112911;
            return new PrioritySelector(ctx => unit = ctx as WoWUnit, ScriptHelpers.CreateInterruptCast(() => unit, volleyOfHatred));
        }

        [EncounterHandler(58803, "Residual Hatred")]
        public Composite ResidualHatredEncounter()
        {
            return
                new PrioritySelector(
                    ScriptHelpers.CreateRunAwayFromBad(
                        ctx => Me.IsRange() || Me.IsMelee() && !Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry != ResidualHatredId,
                        () => ScriptHelpers.Tank.Location,
                        30,
                        17,
                        u => u.Entry == ResidualHatredId && ((WoWUnit)u).HasAura("Ring of Malice") && u.Distance >= 9));
        }


        [EncounterHandler(56884, "Corrupted Taran Zhu", Mode = CallBehaviorMode.Proximity)]
        public Composite CorruptedTaranZhuEncounter()
        {
            const uint TaranZhu = 56884;

            var tankLoc = new WoWPoint(3861.51, 2615.981, 754.5428);
            WoWUnit boss = null;
            int hatred = 0;

            return new PrioritySelector(
                ctx =>
                {
                    hatred = Lua.GetReturnVal<int>("return UnitPower('player',10)", 0);
                    return boss = ctx as WoWUnit;
                },
                new Decorator(ctx => boss.IsFriendly,
                    new PrioritySelector(
                            new Decorator(ctx => boss.QuestGiverStatus == QuestGiverStatus.TurnIn, 
                                ScriptHelpers.CreateTurninQuest(TaranZhu)),

                            new Decorator(ctx => ScriptHelpers.IsBossAlive("Corrupted Taran Zhu"),
                                new Action(ctx => ScriptHelpers.MarkBossAsDead("Corrupted Taran Zhu","He turned friendly")))
                        )),
                    
                new Decorator(
                    ctx => boss.Combat,
                    new PrioritySelector(
                        ScriptHelpers.CreateRunAwayFromBad(
                            ctx => Me.IsRange() || Me.IsDps() && Me.IsMelee() && !Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry != TaranZhu,
                            () => tankLoc,
                            30,
                            17,
                            u => u.Entry == TaranZhu && ((WoWUnit)u).HasAura("Ring of Malice") && u.Distance >= 9),

                // run away from Gripping Hatred if not killing it.
                        ScriptHelpers.CreateRunAwayFromBad(
                            ctx => true,
                            () => tankLoc,
                            30,
                            9,
                            u => u.Entry == GrippingHatred && u.ToUnit().HasAura("Pool of Shadows") && (Targeting.Instance.FirstUnit != u || Me.IsRange())),
                        new Decorator(ctx => hatred >= 45, new Action(ctx => Lua.DoString("ExtraActionButton1:Click()"))),
                        new Decorator(ctx => !boss.HasAura("Ring of Malice"), ScriptHelpers.CreateTankUnitAtLocation(() => tankLoc, 5)))));
        }

        [EncounterHandler(64387, "Master Snowdrift", Mode = CallBehaviorMode.Proximity)]
        public Composite MasterSnowdriftQuestEncounter()
        {
            const uint MasterSnowdriftQuest = 64387;

            WoWUnit boss = null;

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(ctx => boss.IsFriendly && boss.QuestGiverStatus == QuestGiverStatus.TurnIn, ScriptHelpers.CreateTurninQuest(MasterSnowdriftQuest)));
        }

        [ObjectHandler(213888, "Taran Zhu's Personal Stash")]
        [ObjectHandler(213889, "Taran Zhu's Personal Stash")]
        public Composite TaranZhusPersonalStashHandler()
        {
            WoWGameObject chest = null;
            return new PrioritySelector(ctx => chest = ctx as WoWGameObject, new Decorator(ctx => chest.CanLoot, ScriptHelpers.CreateInteractWithObject(() => chest)));
        }
    }
}